package cn.iocoder.yudao.module.ai.utils;

import cn.hutool.core.date.DateUtil;
import cn.iocoder.yudao.module.ai.utils.vo.DataSetFileVo;
import cn.iocoder.yudao.module.ai.utils.vo.MaterialFileVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * 数据集工具类
 */
@Slf4j
@Component
public class DataSetUtil {

    /**
     * 应用名
     */
    @Value("${material.root-path}")
    private String materialRootPath;
    /**
     * zip 文件夹名称
     */
    @Value("${material.source-folder}")
    private String sourceFolderName;
    /**
     * zip解压文件文件夹名称
     */
    @Value("${material.release-folder}")
    private String releaseFolderName;

    /**
     * 公共-上传到本地文件的路径
     */
    @Value("${yudao.file.upload-path}")
    private String commonUploadPath;

    /**
     * 保存原始zip文件
     * @param netFile 网络文件
     * @param diskFile 本地磁盘文件
     * @return [0] 保存zip文件的相对路径；[1] 保存zip文件的绝对路径；[2] 解压文件夹 的相对路径；[3] 解压文件夹 的绝对路径
     * @throws Exception
     */
    public String[] saveZipFile(MultipartFile netFile, File diskFile)throws Exception{
        String[] returnPathArr = new String[4];
        String sourceName = "1.zip";
        String[] folderPathArr = createMaterialSavePath(new AtomicInteger(0)) ;
        log.info("获取到文件保存路径：{}",folderPathArr);
        String materialSourceFolderPath = folderPathArr[1]+"/"+sourceFolderName ;
        // 使用文件输出流将文件保存到服务器路径
        File destinationFile = new File(materialSourceFolderPath+"/"+sourceName);
        if(null != netFile){
            FileUtils.writeByteArrayToFile(destinationFile, netFile.getBytes());
        }else{
            FileUtils.writeByteArrayToFile(destinationFile, FileUtils.readFileToByteArray(diskFile));
        }
        String materialTargetFolderPath = folderPathArr[1]+"/"+releaseFolderName ;
        File releaseFolder = new File(materialTargetFolderPath);
        // 返回相对路径
        returnPathArr[0] = "/"+folderPathArr[0]+"/"+sourceFolderName+"/"+sourceName ;
        returnPathArr[1] = destinationFile.getAbsolutePath() ;
        returnPathArr[2] = "/"+folderPathArr[0]+"/"+releaseFolderName+"/" ;
        returnPathArr[3] = releaseFolder.getAbsolutePath() ;
        return returnPathArr ;
    }

    /**
     * 创建并返回素材文件路径(当前路径必须不存在)
     * [0] 相对路径  [1] 绝对路径
     * @return
     */
    public synchronized String[] createMaterialSavePath(AtomicInteger times)throws Exception{
        // 1 绝对路径 2 目录下的相对路径
        String[] folderPathArr = new String[4];
        String dateStr = DateUtil.format(new Date(), "yyyy-MM-yyyyMMddHHmmssSSS");
        List<String> folderList = new ArrayList<>();
        folderList.addAll(Arrays.asList(dateStr.split("-")));
        // 生成相对路径
        folderPathArr[0] = String.join("/", folderList);
        folderList.add(0,materialRootPath);
        // 生成绝对路径
        String absPath = String.join("/", folderList);
        folderPathArr[1] = absPath;

        // 判断目录是否存在
        File f = new File(absPath);
        if (!f.exists()) {
            boolean createFlag = f.mkdirs();
            log.info("创建素材文件夹：{}，路径={}",createFlag,absPath);
            if(createFlag){
                File sourceFolder = new File(absPath+"/"+sourceFolderName);
                createFlag = sourceFolder.mkdirs();
                log.info("创建素材源文件文件夹：{}，路径={}",createFlag,sourceFolder);
                File releaseFolder = new File(absPath+"/"+releaseFolderName);
                createFlag = releaseFolder.mkdirs();
                log.info("创建素材源解压文件文件夹：{}，路径={}",createFlag,releaseFolder);
                return folderPathArr ;
            }else{
                throw new Exception("创建素材文件夹失败："+absPath);
            }

        }else{
            if(times.incrementAndGet() > 10){
                throw new Exception("获取文件保存路径失败");
            }
            return createMaterialSavePath(times);
        }
    }

    /**
     * 获取视频播放时长
     * @param filePath
     * @return
     */
    public Integer getMp4Seconds(String filePath){
        int seconds = 0;
        FFmpegFrameGrabber grabber = null;
        try {
            grabber = new FFmpegFrameGrabber(filePath);
            grabber.start();
            //这一步获取的是微秒的时间，除以 1000000 获取保留整数部位的秒
            seconds = Math.toIntExact(grabber.getLengthInTime())/1000000;
            log.info("解析文件"+filePath+" 播放时长: " + seconds + " s");
            return seconds; // 返回秒
        } catch (Throwable e) {
            log.warn("读取文件播放时长失败:"+filePath,e);
            return 0;
        } finally {
            if (grabber != null) {
                try {
                    grabber.stop();
                } catch (Exception ignored) {
                }
            }
        }
    }

    /**
     * 文件解压缩
     * @return
     */
    public List<MaterialFileVo> fileUncompressing(String zipFilePath, String targetFolder) throws IOException {
        //返回消息
        List<MaterialFileVo> fileList=new ArrayList<>();
        // 解压文件索引
        int fileIndex = 0;
        // 打开压缩文件
        File sourceFile = new File(zipFilePath);
        ZipFile zipFile = new ZipFile(sourceFile.getAbsoluteFile(), Charset.forName("gbk"));

        // 遍历压缩文件中的所有条目
        Enumeration<? extends ZipEntry> entries = zipFile.entries();
        while (entries.hasMoreElements()) {
            ZipEntry entry = entries.nextElement();

            // 解压缩条目到目标文件夹
            String entryName = entry.getName();
            entryName = new String(entryName.getBytes(StandardCharsets.UTF_8));
            // 扩展名
            String extension = FilenameUtils.getExtension(entryName);
            if (entry.isDirectory()) {
                //entryFile.mkdirs();
            } else {
                // 重新整理写到新的文件路径
                String newFileName = (++fileIndex)+"."+extension;
                File targetFile = new File(targetFolder+"/"+newFileName);
                InputStream inputStream = zipFile.getInputStream(entry);
                OutputStream outputStream = new FileOutputStream(targetFile);
                byte[] buffer = new byte[1024];
                int length;
                while ((length = inputStream.read(buffer)) > 0) {
                    outputStream.write(buffer, 0, length);
                }
                outputStream.flush();
                outputStream.close();
                inputStream.close();
                MaterialFileVo e = new MaterialFileVo();
                e.setFile(targetFile);
                e.setOriginalFileName(entryName);
                e.setOriginalFilePath(entry.getName());
                e.setFileName(newFileName);
                fileList.add(e);
            }
        }
        // 关闭压缩文件
        zipFile.close();
        return fileList;
    }

    /**
     * 文件解压缩
     * @return
     */
    public List<DataSetFileVo> datasetFileUncompressing(String zipFilePath, String targetFolder) throws IOException {
        //返回消息
        List<DataSetFileVo> fileList=new ArrayList<>();
        // 解压文件索引
        int fileIndex = 0;
        // 打开压缩文件
        File sourceFile = new File(zipFilePath);
        ZipFile zipFile = new ZipFile(sourceFile.getAbsoluteFile(), Charset.forName("gbk"));

        // 遍历压缩文件中的所有条目
        Enumeration<? extends ZipEntry> entries = zipFile.entries();
        while (entries.hasMoreElements()) {
            ZipEntry entry = entries.nextElement();

            // 解压缩条目到目标文件夹
            String entryName = entry.getName();
            entryName = new String(entryName.getBytes(StandardCharsets.UTF_8));
            // 扩展名
            String extension = FilenameUtils.getExtension(entryName);
            if (entry.isDirectory()) {
                //entryFile.mkdirs();
            } else {
                // 重新整理写到新的文件路径
                String newFileName = (++fileIndex)+"."+extension;
                File targetFile = new File(targetFolder+"/"+newFileName);
                InputStream inputStream = zipFile.getInputStream(entry);
                OutputStream outputStream = new FileOutputStream(targetFile);
                byte[] buffer = new byte[1024];
                int length;
                while ((length = inputStream.read(buffer)) > 0) {
                    outputStream.write(buffer, 0, length);
                }
                outputStream.flush();
                outputStream.close();
                inputStream.close();
                DataSetFileVo e = new DataSetFileVo();
                e.setExtension(extension);
                e.setFile(targetFile);
                e.setOriginalFileName(entryName);
                e.setOriginalFilePath(entry.getName());
                e.setFileName(newFileName);
                fileList.add(e);
            }
        }
        // 关闭压缩文件
        zipFile.close();
        return fileList;
    }
}
